/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor;

import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

/**
 * 批执行器----批处理
 * 
 * @author Jeff Butler
 */
public class BatchExecutor extends BaseExecutor {

	public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
	/**
	 * 语句列表--Statement链表
	 */
	private final List<Statement> statementList = new ArrayList<Statement>();
	/**
	 * 批执行结果集--batch结果链表
	 */
	private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();
	/**
	 * 当前sql
	 */
	private String currentSql;
	/**
	 * 当前映射的语句
	 */
	private MappedStatement currentStatement;

	public BatchExecutor(Configuration configuration, Transaction transaction) {
		super(configuration, transaction);
	}

	@Override
	public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
		// 获得配置信息
		final Configuration configuration = ms.getConfiguration();
		// 获得StatementHandler
		final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT,
				null, null);
		final BoundSql boundSql = handler.getBoundSql();
		// 获得Sql语句
		final String sql = boundSql.getSql();
		final Statement stmt;
		// 如果sql语句等于当前sql MappedStatement 等于当前Map碰到Statement
		if (sql.equals(currentSql) && ms.equals(currentStatement)) {
			int last = statementList.size() - 1;
			// 获得最后一个
			stmt = statementList.get(last);
			// 有相同的MappedStatement和参数
			BatchResult batchResult = batchResultList.get(last);
			batchResult.addParameterObject(parameterObject);
		} else {
			// 如果不存在就创建一个批处理操作
			Connection connection = getConnection(ms.getStatementLog());
			stmt = handler.prepare(connection);
			currentSql = sql;
			currentStatement = ms;
			// 添加批量处理操作
			statementList.add(stmt);
			batchResultList.add(new BatchResult(ms, sql, parameterObject));
		}
		handler.parameterize(stmt);
		// 最终是调用jdbc的批处理操作
		handler.batch(stmt);
		return BATCH_UPDATE_RETURN_VALUE;
	}

	@Override
	public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
			ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
		Statement stmt = null;
		try {
			flushStatements();
			// 获得配置信息
			Configuration configuration = ms.getConfiguration();
			// 获得StatementHandler
			StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds,
					resultHandler, boundSql);
			// 获得连接
			Connection connection = getConnection(ms.getStatementLog());
			// 获得Statement
			stmt = handler.prepare(connection);
			handler.parameterize(stmt);
			return handler.<E> query(stmt, resultHandler);
		} finally {
			closeStatement(stmt);
		}
	}

	/* 刷新Statement，记录执行次数 */
	@Override
	public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
		try {
			List<BatchResult> results = new ArrayList<BatchResult>();
			if (isRollback) {
				return Collections.emptyList();
			}
			// 如果进行了批量处理
			for (int i = 0, n = statementList.size(); i < n; i++) {
				Statement stmt = statementList.get(i);
				BatchResult batchResult = batchResultList.get(i);
				try {
					// 记录批量处理执行操作的条数
					batchResult.setUpdateCounts(stmt.executeBatch());
					MappedStatement ms = batchResult.getMappedStatement();
					// 参数对象集合
					List<Object> parameterObjects = batchResult.getParameterObjects();
					// 生成key
					KeyGenerator keyGenerator = ms.getKeyGenerator();
					if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
						Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
						jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
					} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { // issue
																						// #141
						for (Object parameter : parameterObjects) {
							keyGenerator.processAfter(this, ms, stmt, parameter);
						}
					}
				} catch (BatchUpdateException e) {
					StringBuilder message = new StringBuilder();
					message.append(batchResult.getMappedStatement().getId()).append(" (batch index #").append(i + 1)
							.append(")").append(" failed.");
					if (i > 0) {
						message.append(" ").append(i)
								.append(" prior sub executor(s) completed successfully, but will be rolled back.");
					}
					throw new BatchExecutorException(message.toString(), e, results, batchResult);
				}
				//记录操作  
				results.add(batchResult);
			}
			return results;
		} finally {
			for (Statement stmt : statementList) {
				closeStatement(stmt);
			}
			currentSql = null;
			statementList.clear();
			batchResultList.clear();
		}
	}

}
